#include "pct.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#include <pthread.h>
#include <csignal>

using namespace std;

#define MAX_SEC_NUMBER 256
#define TAMANIO_PAQUETE 256
#define TIMEOUT_BACKGROUND 6


/****************Variables generales*************************/
struct in_addr global_localIPaddr;
struct in_addr global_peerIPaddr;
struct pct_header global_pct_header;
struct sockaddr_in saddr; 
int s; // esto es para el socket
const int TAMANIO_ENVIO = TAMANIO_PAQUETE - sizeof(struct iphdr) - sizeof(struct pct_header);
pthread_t thread;
int iret;
int esperandoSaludo=0;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
bool soyServidor;
bool conexionTerminada;

/****************Variables para conexion PCT*************************/
socklen_t *len;
int fromlen;
unsigned char nro_SEC_servidor, nro_SEC_cliente, nextSeqNum;
struct sockaddr_in daddr;
struct cacheServidor{
	char buf[TAMANIO_ENVIO];
	size_t len;
	struct pct_header header;
};
list<struct cacheServidor*> bufferServidor;


/************************DEBUG (despues hay que borrar esto)*************/
char charIP[20];

int IP2asc(u_int32_t IP,char * result){
	int aux;
	aux = sprintf(result, "%u.%u.%u.%u", (IP / 16777216),((IP / 65536) % 256),((IP / 256) % 256),(IP % 256));
	return aux;
}

/***************************************************************************************************************************/
/****************Funciones auxiliares***************************************************************************************/
/***************************************************************************************************************************/

int mandarPaquete(const void *buf, size_t len,struct pct_header* header){	
	// variables para mandar el mensaje
	struct iphdr *ip;
	int j = sizeof(struct iphdr) + sizeof(struct pct_header);
	char packet[len + j];
	ip = (struct iphdr *)packet;
	memset(packet, 0, sizeof(packet));
	//////////////////////////////

	ip->ihl = 5;
	ip->version = 4;
	ip->tos = 0;
	ip->tot_len = htons(40);	/* 16 byte value */
	ip->frag_off = 0;		/* no fragment */
	ip->ttl = 64;			/* default value */
	ip->protocol = IPPROTO_RAW;	/* protocol at L4 */
	ip->check = 0;			/* not needed in iphdr */
//	ip->saddr = saddr.sin_addr.s_addr;
	ip->saddr= global_localIPaddr.s_addr;
 	ip->daddr = daddr.sin_addr.s_addr;

	memcpy(packet + sizeof(struct iphdr), header, sizeof(struct pct_header));	
	
	int ret;
	
	for(int i = 0; i < len; i++){
		packet[j] = ((char *)buf)[i];
		j++;
	}	
	
	if ((ret = sendto(s, (char *)packet, sizeof(packet), 0, (struct sockaddr *)&daddr, (socklen_t)sizeof(daddr))) < 0){
		perror("packet send error:");
		return -1;
	}	
	
	return len;
}

int recibirPaquete(void *buf, struct pct_header* header, unsigned long &ip_origen){
    // variables para el select
	fd_set input;
	struct timeval timeout;
	FD_ZERO(&input);
	FD_SET(s, &input);
	int max_fd = s + 1;
	/////////////////////////
    // variables para recibir el paquete
	char packet[TAMANIO_PAQUETE];
	int ret;
	int j;
	bool termine = false;
	int paraMiPuerto;
	int paraMiIP;
	int desdeIPCorrecto;
	int desdePuertoCorrecto;
	char miIPchar[20], miIPheaderChar[20], suIPchar[20], suIPheaderChar[20];
	///////////////////////////////////

	while(!termine){
		timeout.tv_sec  = TIMEOUT_BACKGROUND;
		timeout.tv_usec = 0;
		int n = select(max_fd, &input, NULL, NULL, &timeout);
		if (n < 0){
			perror("select failed\n");
			return -1;
		}else if (n == 0){
			perror("timeout recibirPaquete\n");
			return -2;//OJO
		}else{
			memset((char *)packet, 0, TAMANIO_PAQUETE);
			if ((ret = recvfrom(s, (char *)&packet, TAMANIO_PAQUETE, 0, (struct sockaddr *)&saddr, (socklen_t *)&fromlen)) < 0){
				perror("packet receive error:");
				return -1;
			}
			
			struct iphdr *ipr = (struct iphdr *)packet;
			memcpy(header, packet + sizeof(struct iphdr), sizeof(struct pct_header)); // guardo la informacion del pct_header
			
			paraMiPuerto = header->destPort == global_pct_header.srcPort;
			desdePuertoCorrecto = header->srcPort == global_pct_header.destPort;

            // transformo los IP a char
			IP2asc(ntohl(ipr->saddr),&suIPheaderChar[0]);						
			IP2asc(ntohl(ipr->daddr),&miIPheaderChar[0]);			
			IP2asc(ntohl(global_peerIPaddr.s_addr),&suIPchar[0]);			
			IP2asc(ntohl(global_localIPaddr.s_addr),&miIPchar[0]);		
			
			paraMiIP = strcmp(miIPheaderChar,miIPchar) == 0;
			desdeIPCorrecto = strcmp(suIPchar,suIPheaderChar) == 0;			
			
			if((!esperandoSaludo && paraMiPuerto && paraMiIP && desdeIPCorrecto && desdePuertoCorrecto) ||
			(esperandoSaludo && paraMiPuerto && paraMiIP)){ // me parece que hay que agregar de alguna 		forma la condicion && (header->srcPort == global_pct_header.destPort)
				termine = true;
				ip_origen = ipr->saddr;					
				int i = sizeof(struct iphdr) + sizeof(struct pct_header);
				j = 0;
				while (i < ret) {						
					((char *)buf)[j] = ((char *)packet)[i];
					i++;
					j++;
				}
				
			}
			
		}
	}
	return j;
}

int mandarPaqueteSeguro(const void *buf, size_t len,struct pct_header* header){
	// if(((nextSeqNum + MAX_SEC_NUMBER -1)% MAX_SEC_NUMBER) == ((nro_SEC_cliente + GBN_WINDOW)% MAX_SEC_NUMBER)){
		// return 0;
	// }
	pthread_mutex_lock(&mutex1); // pido el lock
	if(bufferServidor.size() >= GBN_WINDOW){
		pthread_mutex_unlock(&mutex1); // libero el lock
		return 0;
	}
	struct cacheServidor* nuevoElem = new struct cacheServidor;
	memcpy(nuevoElem->buf, (char*)buf, len);
	nuevoElem->len = len;	
	nuevoElem->header.srcPort = header->srcPort;	
	nuevoElem->header.destPort = header->destPort;	
	nuevoElem->header.flags_pct = header->flags_pct; 
	nuevoElem->header.nro_SEC_pct = header->nro_SEC_pct; 
	nuevoElem->header.nro_ACK_pct = header->nro_ACK_pct;
	
	bufferServidor.push_back(nuevoElem); // agrego el nuevo elemento al buffer
	
	nextSeqNum = (nextSeqNum+1) % MAX_SEC_NUMBER;
	pthread_mutex_unlock(&mutex1); // libero el lock
	return  mandarPaquete(buf, len, header);
}

int recibirPaqueteSeguro(void *buf, struct pct_header* header, unsigned long &ip_origen){	
	int ret = recibirPaquete(buf, header, ip_origen);
	if(ret < 0){
		return ret;
	}
	if(((header->flags_pct & DATA_FLAG) == DATA_FLAG) && ret == 0){
		return ret;
	}else if(((header->flags_pct & DATA_FLAG) == DATA_FLAG) || ((header->flags_pct & FIN_FLAG) == FIN_FLAG)){
		printf("\nNRO paquete recibido: %u\n", header->nro_SEC_pct);
		printf("NRO paquete esperado: %u\n", nro_SEC_cliente);
		if(header->nro_SEC_pct == nro_SEC_cliente){
			nro_SEC_cliente = (nro_SEC_cliente + 1) % MAX_SEC_NUMBER;
			if(((header->flags_pct & FIN_FLAG) == FIN_FLAG)){
				ret = -4;
			}
		}else{		
			ret = -3;
		}
		struct pct_header header_cliente;
		header_cliente.srcPort = global_pct_header.srcPort;	
		header_cliente.destPort = global_pct_header.destPort;	
		header_cliente.flags_pct = ACK_FLAG;
		header_cliente.nro_SEC_pct = 0; 
		header_cliente.nro_ACK_pct = nro_SEC_cliente;	
		mandarPaquete(NULL, 0, &header_cliente);
	}
	return ret;
}

void reenviarTodosLosPaquetesEnVentana(){
	list<struct cacheServidor*>::iterator i;
	for(i = bufferServidor.begin(); i != bufferServidor.end(); i++){
		mandarPaquete((*i)->buf, (*i)->len, &((*i)->header));
	}
}

void *proceso_background(void *ptr) {
	int cantVecesSeguidasTimeout = 0;
	bool finPCT = false;
	// inicializo todo lo relacionadoa al select()
	fd_set input;
	struct timeval timeout;
	FD_ZERO(&input);	
	FD_SET(s, &input);
	int max_fd = s + 1;	
	perror("Entro al background\n");
	while(!finPCT){
		struct pct_header header;
		header.flags_pct = 0;
		unsigned long ip_origen;
		char bufaux[TAMANIO_ENVIO];		
		int ret = recibirPaquete(bufaux, &header, ip_origen);		
		if(ret >= 0){
			pthread_mutex_lock(&mutex1); // pido el lock
			if(((header.flags_pct & ACK_FLAG) == ACK_FLAG) && (((nro_SEC_servidor + 1) % MAX_SEC_NUMBER) == header.nro_ACK_pct)){
				printf("\nNRO ACK recibido: %u\n", header.nro_ACK_pct);
				printf("NRO ACK esperado: %u\n", nro_SEC_servidor);
				nro_SEC_servidor = (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
				struct cacheServidor* aux = bufferServidor.front();
				delete aux;
				bufferServidor.pop_front();
				perror("TOY ACA MUNIANIO! TODO OK\n");
			}else if(((header.flags_pct & ACK_FLAG) == ACK_FLAG) && (nro_SEC_servidor == header.nro_ACK_pct)){
				perror("Tengo q reenviar todo\n");
				reenviarTodosLosPaquetesEnVentana();
			}else if(((header.flags_pct & ACK_FLAG) == ACK_FLAG) && (nro_SEC_servidor < header.nro_ACK_pct) && ( nro_SEC_servidor > (header.nro_ACK_pct - GBN_WINDOW*3))){
				printf("\nNRO ACK recibido: %u\n", header.nro_ACK_pct);
				printf("NRO ACK esperado: %u\n", nro_SEC_servidor);
				while(nro_SEC_servidor < header.nro_ACK_pct){
					nro_SEC_servidor = (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
					struct cacheServidor* aux = bufferServidor.front();
					delete aux;
					bufferServidor.pop_front();
					perror("TOY ACA MUNIANIO! TODO OK2\n");
				}
			}else if(((header.flags_pct & ACK_FLAG) == ACK_FLAG) && (((nro_SEC_servidor + GBN_WINDOW) / MAX_SEC_NUMBER) > 0) && (((nro_SEC_servidor + GBN_WINDOW) % MAX_SEC_NUMBER) > header.nro_ACK_pct)){
				printf("\nNRO ACK recibido: %u\n", header.nro_ACK_pct);
				printf("NRO ACK esperado: %u\n", nro_SEC_servidor);
				while(nro_SEC_servidor != 0){
					nro_SEC_servidor = (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
					struct cacheServidor* aux = bufferServidor.front();
					delete aux;
					bufferServidor.pop_front();
					perror("TOY ACA MUNIANIO! TODO OK3\n");
				}
				while(nro_SEC_servidor < header.nro_ACK_pct){
					nro_SEC_servidor = (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
					struct cacheServidor* aux = bufferServidor.front();
					delete aux;
					bufferServidor.pop_front();
					perror("TOY ACA MUNIANIO! TODO OK4\n");
				}
			}else if(((header.flags_pct & FIN_FLAG) == FIN_FLAG)){
				perror("FIN background\n");
				finPCT = true;
			}else if(((header.flags_pct & DATA_FLAG) == DATA_FLAG) && (ret == 0)){
				printf("Llego paquete keep-alive\n");
				cantVecesSeguidasTimeout = 0;
			}else{ // este paquete no es para mi o llego fuera de orden
				printf("\nNRO ACK recibido: %u\n", header.nro_ACK_pct);
				printf("NRO ACK esperado: %u\n", nro_SEC_servidor);
				perror("problema con el ACK en background\n");
			}
			pthread_mutex_unlock(&mutex1); // libero el lock
			cantVecesSeguidasTimeout = 0;
		}else if(ret == -2){
			cantVecesSeguidasTimeout++;
			perror("timeout background\n");
			pthread_mutex_lock(&mutex1); // pido el lock
			if(bufferServidor.size() > 0){
				reenviarTodosLosPaquetesEnVentana();
			}else{
				struct pct_header header;
				header.srcPort = global_pct_header.srcPort;	
				header.destPort = global_pct_header.destPort;	
				header.flags_pct = DATA_FLAG;
				header.nro_SEC_pct = 0;
				header.nro_ACK_pct = 0;
				mandarPaquete(NULL, 0, &header);
			}
			pthread_mutex_unlock(&mutex1); // libero el lock
		}else{
			perror("select failed\n");
		}
		
		if(cantVecesSeguidasTimeout > 2){
			finPCT = true;			
		}		
	}
	conexionTerminada = true;
}

/***************************************************************************************************************************/
/****************Funciones del programa*************************************************************************************/
/***************************************************************************************************************************/

int crearPCT(struct in_addr localIPaddr){
	global_localIPaddr = localIPaddr;
	char ipChar[20];
	srand (time(NULL));
	
	return 0;
}
int aceptarPCT(unsigned char localPCTport){
	soyServidor = true;	
	
	global_pct_header.srcPort = localPCTport;
	if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
		perror("error:");
		return -1;
	}
	//memset(packet, 0, sizeof(packet));
	len = (socklen_t *)sizeof(saddr);
	fromlen = sizeof(saddr);
	
	saddr.sin_family = AF_INET;
	saddr.sin_port = 0; /* not needed in SOCK_RAW */	
	saddr.sin_addr.s_addr = global_localIPaddr.s_addr;
	char ipChar[20];
	IP2asc(ntohl(saddr.sin_addr.s_addr),&ipChar[0]);
	fprintf(stderr,"En ACEPTAR pongo en saddr : %s\n ",ipChar);
	memset(saddr.sin_zero, 0, sizeof(saddr.sin_zero));
	
	unsigned long ip_origen;
	
	// Espero al primer paquete del three-way-handshake del cliente
	esperandoSaludo = 1;
	struct pct_header header_cliente;
	char buf[TAMANIO_PAQUETE];
	int esperandoPrimerMsj = 1;
	while (esperandoPrimerMsj){
		int aux = recibirPaquete(buf, &header_cliente, ip_origen);
		if (aux == -1){
			return -1;
		}else if ((aux >= 0) && (header_cliente.flags_pct==SYN_FLAG)){
			esperandoPrimerMsj = 0;	
		}
	}
		
	global_peerIPaddr.s_addr = ip_origen;
	// uso la IP del cliente para conectarme
	daddr.sin_family = AF_INET;
	daddr.sin_port = 0; /* not needed in SOCK_RAW */
	daddr.sin_addr.s_addr = global_peerIPaddr.s_addr;
	memset(daddr.sin_zero, 0, sizeof(daddr.sin_zero));
	
	
	nro_SEC_servidor = header_cliente.nro_SEC_pct; // agarro el numero de secuencia del cliente
	nro_SEC_servidor = (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
	global_pct_header.destPort = header_cliente.srcPort; // agarro el puerto del cliente
	esperandoSaludo=0;
	int estableciendoConexionServidor = 1;
	// nro_SEC_cliente = rand() % MAX_SEC_NUMBER; // elijo al azar el primero numero de secuencia del servidor
	nro_SEC_cliente = header_cliente.nro_SEC_pct; // elijo al azar el primero numero de secuencia del servidor
	while  (estableciendoConexionServidor){
	// Le respondo con el segundo paquete
		 
		
		struct pct_header header_servidor;
		header_servidor.srcPort = global_pct_header.srcPort;	
		header_servidor.destPort = global_pct_header.destPort;	
		header_servidor.flags_pct = SYN_FLAG | ACK_FLAG; // seteo la flag en SYN y en ACK
		header_servidor.nro_SEC_pct = nro_SEC_cliente-1; // mando el primer numero de secuencia del servidor
		header_servidor.nro_ACK_pct = nro_SEC_servidor;	// le mando el numero de secuancia del cliente +1 (acepto la conexion)
		if (mandarPaquete(NULL, 0, &header_servidor) < 0){
			return -1;
		}
		// nro_SEC_servidor = nro_SEC_servidor++;//aumento el numero esperado desde el otro peer
		// Espero al tercer paquete del three-way-handshake del cliente y retorno
		int rec = recibirPaquete(buf, &header_cliente, ip_origen);
		if (rec < 0 && rec != -2){
			return -1;
		}
		fprintf(stderr,"nro_SEC_cliente: %i\n",nro_SEC_cliente);
		fprintf(stderr,"nro_ACK_cliente: %i \n", header_cliente.nro_ACK_pct);
		if (header_cliente.flags_pct == DATA_FLAG) {
			fprintf(stderr,"DATA FLAG nro_SEC_cliente: %i \n",nro_SEC_cliente);			
		}
			
		if(rec >= 0){//Mensaje correctamente recibido
			if((header_cliente.flags_pct == ACK_FLAG && header_cliente.nro_ACK_pct == nro_SEC_cliente )
				|| (header_cliente.flags_pct == DATA_FLAG &&  header_cliente.nro_ACK_pct== ((nro_SEC_cliente + 1) % MAX_SEC_NUMBER ))){ // agrego esto tambien para que acepte el primer mensaje de la conexion como cierrre del saludo
				
				if(header_cliente.flags_pct == DATA_FLAG &&  header_cliente.nro_ACK_pct== ((nro_SEC_cliente + 1) % MAX_SEC_NUMBER)){
					fprintf(stderr,"TERCER PAQUETE NUNCA LLEGO\n");
					fprintf(stderr,"nro_SEC_cliente: %i\n", nro_SEC_cliente);
					fprintf(stderr,"header_cliente.nro_ACK_pct: %i\n", header_cliente.nro_ACK_pct);
					
				}
				
				estableciendoConexionServidor = 0;
				nro_SEC_cliente = (nro_SEC_cliente + 1) % MAX_SEC_NUMBER;
				nro_SEC_servidor = (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
			}
		}
		if(estableciendoConexionServidor){
			fprintf(stderr,"REENVIO SEGUNDO PAQUETE\n");
			// nro_SEC_cliente = (nro_SEC_cliente + 1) % MAX_SEC_NUMBER;
		}	
	}
	conexionTerminada = false;
	return 0; // todo OK
}
int conectarPCT(unsigned char localPCTport,unsigned char peerPCTPport, struct in_addr peerIPaddr){	
	soyServidor = false;
	unsigned long ip_origen;
	
	global_pct_header.srcPort = localPCTport;
	global_pct_header.destPort = peerPCTPport;
	global_peerIPaddr = peerIPaddr;
	
	
	if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
		perror("error:");
		return -1;
	}
	
	saddr.sin_family = AF_INET;
	saddr.sin_port = 0; /* not needed in SOCK_RAW */
	// inet_pton(AF_INET, ORIG, (struct in_addr *)&saddr.sin_addr.s_addr); // ESTO HAY QUE CAMBIARLO!!!!
	saddr.sin_addr.s_addr = global_localIPaddr.s_addr;
	
	//saddr.sin_addr.d_addr = global_peerIPaddr.s_addr;//mine
	
	memset(saddr.sin_zero, 0, sizeof(saddr.sin_zero));
	daddr.sin_family = AF_INET;
	daddr.sin_port = 0; /* not needed in SOCK_RAW */
	//inet_pton(AF_INET, DEST, (struct in_addr *)&daddr.sin_addr.s_addr);
	daddr.sin_addr.s_addr = global_peerIPaddr.s_addr;
	memset(daddr.sin_zero, 0, sizeof(daddr.sin_zero));	
	
	// Mando el primer paquete de three-way-handshake
	nro_SEC_cliente = rand() % MAX_SEC_NUMBER; // elijo al azar el primero numero de secuencia del cliente 
	
	struct pct_header header_cliente;
	header_cliente.srcPort = global_pct_header.srcPort;	
	header_cliente.destPort = global_pct_header.destPort;	
	header_cliente.flags_pct = SYN_FLAG; // seteo la flag en SYN
	header_cliente.nro_SEC_pct = nro_SEC_cliente; // primer numero de secuencia del cliente
	header_cliente.nro_ACK_pct = 0;	// esto no me importa
	int llegoSYN = 0;
	while (!llegoSYN){
		
		if (mandarPaquete(NULL, 0, &header_cliente) < 0){
			return -1;
		}
		
		int esperandoReconocimiento = 1;
		while (esperandoReconocimiento) {
		// Espero a recibir la respuesta del servidor
			struct pct_header header_servidor;
			char buf[TAMANIO_PAQUETE];
			int estadoRecibir = recibirPaquete(buf, &header_servidor, ip_origen);
			if (estadoRecibir >= 0){
				fprintf(stderr,"header_cliente.nroACK: %i \n", header_servidor.nro_ACK_pct);
				fprintf(stderr,"nro_SEC_cliente: %i \n", nro_SEC_cliente);
				if (header_servidor.nro_ACK_pct == ((nro_SEC_cliente + 1) % MAX_SEC_NUMBER) && (header_servidor.flags_pct== SYN_FLAG | ACK_FLAG)){ //CONTROLO QUE LLEGUE CON EL NUMERO DE ACK Adecuado Y LAS BANDERAS ADECUADAS
					nro_SEC_servidor = header_servidor.nro_SEC_pct; // primer numero de secuencia del servidor
					nro_SEC_servidor = (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
					nro_SEC_cliente = (nro_SEC_cliente + 1) % MAX_SEC_NUMBER;
					
					esperandoReconocimiento = 0;
					llegoSYN=1;
					fprintf(stderr,"RECIBI SEGUNDO MENSAJE ADECUADAMENTE\n");
				}
			} else if(estadoRecibir == -2){
				fprintf(stderr,"timeout esperando el segundo paquete\n");
				esperandoReconocimiento = 0;
			} else {
				return -1;
			}
		}
		if (!llegoSYN) {
			fprintf(stderr,"NO LLEGO SYN\n");			
		}
	}
	
	// Le mando el tercer paquete del three-way-handshake con la confirmacion y retorno
	header_cliente.srcPort = global_pct_header.srcPort;	
	header_cliente.destPort = global_pct_header.destPort;	
	header_cliente.flags_pct = ACK_FLAG; // seteo la flag en ACK
	header_cliente.nro_SEC_pct = nro_SEC_cliente; // Envio el numero de secuencia cliente que espera
	header_cliente.nro_ACK_pct = nro_SEC_servidor;	// le mando el numero de secuancia del servidor +1
	if (mandarPaquete(NULL, 0, &header_cliente) < 0){
		return -1;
	}
	nro_SEC_cliente = (nro_SEC_cliente + 1) % MAX_SEC_NUMBER; //incremento el numero de secuencia esperado
	nro_SEC_servidor= (nro_SEC_servidor + 1) % MAX_SEC_NUMBER;
	nextSeqNum = nro_SEC_servidor;
	fprintf(stderr,"nextSeqNum: %i\n", nextSeqNum);	
	iret = pthread_create(&thread, NULL, proceso_background, (void*) NULL);
	conexionTerminada = false;
	return 0;
} 	
int escribirPCT(const void *buf, size_t len){
	if(conexionTerminada){
		return -1;
	}
	struct pct_header header;
	header.srcPort = global_pct_header.srcPort;	
	header.destPort = global_pct_header.destPort;	
	header.flags_pct = DATA_FLAG;		
	header.nro_ACK_pct = nextSeqNum; //esto sirve por si se pierde el 3er paquete del three way handshake
	int ret = 0;
	int i = 0;
	bool termine = false;
	
	while((ret < len) && !termine){
		header.nro_SEC_pct = nextSeqNum;
		int largo = TAMANIO_ENVIO;
		if((len - ret) < TAMANIO_ENVIO){
			largo = len - ret;			
		}
		// ret += mandarPaquete((char*)buf + TAMANIO_ENVIO*i, largo, &header);
		int retaux = mandarPaqueteSeguro((char*)buf + TAMANIO_ENVIO*i, largo, &header);
		if(retaux > 0){
			ret += retaux;
			i++;
		}else{
			termine = true;
		}
		if(conexionTerminada){
			termine = true;
		}
	}	
	return ret;
}
int leerPCT(void *buf, size_t len){
	if(conexionTerminada){
		return -1;
	}
	unsigned long ip_origen;
	struct pct_header header;
	int ret = 0;
	header.flags_pct = 0;
	int i = 0;
	bool termine = false;
	int cantVecesSeguidasTimeout = 0;
	while(((ret + TAMANIO_ENVIO) <= len) && !termine){
		char bufaux[TAMANIO_ENVIO];		
		int retaux = recibirPaqueteSeguro(bufaux, &header, ip_origen);
		if(retaux == -1){
			termine = true;
		}else if(retaux >= 0){
			if((retaux > 0) && (header.flags_pct & DATA_FLAG) == DATA_FLAG){				
				memcpy((char*)buf + ret, bufaux, retaux);
				ret += retaux;
			}else if(((header.flags_pct & DATA_FLAG) == DATA_FLAG) && retaux == 0){
				printf("Llego paquete keep-alive\n");
				cantVecesSeguidasTimeout = 0;
			}
			i++;
			cantVecesSeguidasTimeout = 0;
		}else if(retaux == -2){
			cantVecesSeguidasTimeout++;
			
			struct pct_header header;
			header.srcPort = global_pct_header.srcPort;	
			header.destPort = global_pct_header.destPort;	
			header.flags_pct = DATA_FLAG;
			header.nro_SEC_pct = 0;
			header.nro_ACK_pct = 0;
			mandarPaquete(NULL, 0, &header);
		}else if(retaux == -3){
			cantVecesSeguidasTimeout = 0;
		}else if((retaux == -4) && ((header.flags_pct & FIN_FLAG) == FIN_FLAG)){
			header.srcPort = global_pct_header.srcPort;	
			header.destPort = global_pct_header.destPort;	
			header.flags_pct = FIN_FLAG;
			header.nro_SEC_pct = nro_SEC_servidor;	
			header.nro_ACK_pct = 0;
			
			mandarPaquete(NULL, 0, &header);
			termine = true;
			conexionTerminada = true;
			if(ret == 0){
				return -1;
			}
		}
		if(cantVecesSeguidasTimeout > 2){
			termine = true;
			conexionTerminada = true;
		}
	}
	return ret;
}
int cerrarPCT(){
	conexionTerminada = true;
	if(!soyServidor){
		struct pct_header header;
		header.srcPort = global_pct_header.srcPort;	
		header.destPort = global_pct_header.destPort;	
		header.flags_pct = FIN_FLAG;
		header.nro_SEC_pct = nextSeqNum;
		header.nro_ACK_pct = 0;
		
		mandarPaqueteSeguro(NULL, 0, &header);
		pthread_join(thread, NULL);
	}else{
		struct pct_header header;
		header.srcPort = global_pct_header.srcPort;	
		header.destPort = global_pct_header.destPort;	
		header.flags_pct = FIN_FLAG;
		header.nro_SEC_pct = nro_SEC_servidor;	
		header.nro_ACK_pct = 0;
		mandarPaquete(NULL, 0, &header);
	}
	return 0;
}
